using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Shapes;
using System.Windows.Media;
using WozLib;
using System.Windows;
using System.Diagnostics;
using WozLib.UI.Ink.SketchCommands;

using WozLib.UI.Ink.SketchElements;
using WozLib.UI.Ink.SketchObjects;
using System.Windows.Documents;
using WozLib.UI.Ink.Adorners;

namespace WozLib.UI.Ink
{
  public class ClientInkCanvas : SketchInkCanvas
  {
    public bool Suspended
    {
      get { return _Suspended; }
      set { _Suspended = value; }
    } private bool _Suspended;

    public ClientInkCanvas()
    {
      // Setup Ink Canvas      

      //this.Background = Brushes.Transparent;

      //this.EditingMode = InkCanvasEditingMode.None;

      this.DefaultDrawingAttributes.FitToCurve = false;
      this.DefaultDrawingAttributes.Width = 2;
      this.DefaultDrawingAttributes.Height = 2;

      this.EditingMode = InkCanvasEditingMode.Select;
      this.EditingModeChanged += new RoutedEventHandler(ClientInkCanvas_EditingModeChanged);

      //if (this.IsGestureRecognizerAvailable)
      //{
      //  this.Gesture += new InkCanvasGestureEventHandler(ClientInkCanvas_Gesture);
      //  this.SetEnabledGestures(new ApplicationGesture[] {ApplicationGesture.AllGestures});
      //}


      this.StrokeCollected += new InkCanvasStrokeCollectedEventHandler(ClientInkCanvas_StrokeCollected);
      this.SelectionMoved += new EventHandler(ClientInkCanvas_SelectionMoved);
      this.SelectionResized += new EventHandler(ClientInkCanvas_SelectionResized);
      this.SelectionChanging += new InkCanvasSelectionChangingEventHandler(ClientInkCanvas_SelectionChanging);

      this.StrokeErasing += new InkCanvasStrokeErasingEventHandler(ClientInkCanvas_StrokeErasing);

      this.PreviewMouseDown += new MouseButtonEventHandler(ClientInkCanvas_PreviewMouseDown);

      this.PreviewMouseUp += new MouseButtonEventHandler(ClientInkCanvas_PreviewMouseUp);

      this.MouseUp += new MouseButtonEventHandler(ClientInkCanvas_MouseUp);

      //this.PreviewMouseMove += new MouseEventHandler(ClientInkCanvas_PreviewMouseMove);
      this.MouseMove += new MouseEventHandler(ClientInkCanvas_MouseMove);

      // When the user current tool changes
      // we change to that tool because ClientInkCanvas is the "user" in the Wizard of Oz system
      this.UserCurrentInkToolChanged += new UserCurrentInkToolChangedHandler(ClientInkCanvas_UserCurrentInkToolChanged);
    }

    //void ClientInkCanvas_Gesture(object sender, InkCanvasGestureEventArgs e)
    //{
    //  Console.WriteLine("Gesture Strokes: " + e.Strokes.Count.ToString());
    //}

    void ClientInkCanvas_EditingModeChanged(object sender, RoutedEventArgs e)
    {
      // just a debug
      //Console.WriteLine("(client) Editng mode now: " + this.EditingMode.ToString());
    }

    void ClientInkCanvas_UserCurrentInkToolChanged(SketchPad.InkTools newInkTool, SketchPad.InkTools previousInkTool)
    {
      switch (newInkTool)
      {
        case SketchPad.InkTools.Pencil:
          this.EditingMode = InkCanvasEditingMode.Ink;
          break;
        case SketchPad.InkTools.Eraser:
          this.EditingMode = InkCanvasEditingMode.EraseByStroke;
          break;
        case SketchPad.InkTools.Selector:
          this.EditingMode = InkCanvasEditingMode.Select;
          break;
        case SketchPad.InkTools.Gesture:
          this.EditingMode = InkCanvasEditingMode.GestureOnly;
          break;
        case SketchPad.InkTools.None:
          this.EditingMode = InkCanvasEditingMode.None;
          break;

          // the rest of these are not currently used in the client side

        case SketchPad.InkTools.Line:
          break;
        case SketchPad.InkTools.Rectangle:
          break;
        case SketchPad.InkTools.Ellipse:
          break;
        case SketchPad.InkTools.Image:
          break;
        case SketchPad.InkTools.Text:
          break;
        case SketchPad.InkTools.Button:
          break;
        case SketchPad.InkTools.Arrow:
          break;
        case SketchPad.InkTools.Gradient:
          break;
        default:
          break;
      }
    }

    void ClientInkCanvas_StrokeErasing(object sender, InkCanvasStrokeErasingEventArgs e)
    {
      if (e.Stroke is ISketchObject)
      {

        // create erase command
        EraseSketchCommand eraseCommand = new EraseSketchCommand((e.Stroke as ISketchObject).GetWozObject());

        // push erase command
        NotifyCommandOccured(eraseCommand);
      }
    }

    private List<SketchObject> _PreviousSelectedWozObjects = new List<SketchObject>();
    void ClientInkCanvas_SelectionChanging(object sender, InkCanvasSelectionChangingEventArgs e)
    {
      lock (_PreviousSelectedWozObjects)
      {
        List<SketchObject> nextSelectedWozObjects = new List<SketchObject>();
        // make sure only "selectable" things get selected
        foreach (UIElement uie in e.GetSelectedElements())
        {
          if (uie is ISketchObject)
          {
            // ToDo: also check to see if it's selectable property == true
            nextSelectedWozObjects.Add((uie as ISketchObject).GetWozObject());
          }
        }
        foreach (Stroke stroke in e.GetSelectedStrokes())
        {
          if (stroke is ISketchObject)
          {
            nextSelectedWozObjects.Add((stroke as ISketchObject).GetWozObject());
          }
        }

        // ToDo: set what will be selected based on our list
        //e.SetSelectedElements(

        // create selection change command
        ChangeSelectionSketchCommand selectionCommand = new ChangeSelectionSketchCommand(nextSelectedWozObjects, _PreviousSelectedWozObjects);

        // push selection change command
        NotifyCommandOccured(selectionCommand);

        // reset our list
        _PreviousSelectedWozObjects = nextSelectedWozObjects;
      }
    }

    void ClientInkCanvas_SelectionResized(object sender, EventArgs e)
    {
      SelectionModified();
    }

    void ClientInkCanvas_SelectionMoved(object sender, EventArgs e)
    {
      SelectionModified();
    }

    private void SelectionModified()
    {
      List<SketchObject> previousWozObject = new List<SketchObject>();
      List<SketchObject> nextWozObject = new List<SketchObject>();

      foreach (Stroke stroke in this.Strokes)
      {
        if (stroke is ISketchObject)
        {
          ISketchObject iWozObject = stroke as ISketchObject;

          // capture previous state
          previousWozObject.Add(iWozObject.GetWozObject());

          // sync state
          iWozObject.SyncProperties();

          //capture new state
          nextWozObject.Add(iWozObject.GetWozObject());
        }
      }
      foreach (UIElement uie in this.Children)
      {
        if (uie is ISketchObject)
        {
          ISketchObject iWozObject = uie as ISketchObject;

          // capture previous state
          previousWozObject.Add(iWozObject.GetWozObject());

          // sync state
          iWozObject.SyncProperties();

          //capture new state
          nextWozObject.Add(iWozObject.GetWozObject());
        }
      }

      // create modification command
      ModifyObjectSketchCommand modifyCommand = new ModifyObjectSketchCommand(nextWozObject, previousWozObject);

      // push modification command
      NotifyCommandOccured(modifyCommand);
    }

    void ClientInkCanvas_MouseUp(object sender, MouseButtonEventArgs e)
    {
      //Debug.WriteLine("ClientInkCanvas_MouseUp");
    }

    void ClientInkCanvas_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
      //Debug.WriteLine("ClientInkCanvas_PreviewMouseUp");
    }

    void ClientInkCanvas_MouseMove(object sender, MouseEventArgs e)
    {
      // determine if mouse button is down
      bool mouseOrPenDown = (e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed ||
        e.MiddleButton == MouseButtonState.Pressed);

      // get the mouse position relative to the ink canvas
      Point pointRelativeToInkCanvas = e.GetPosition(this);
      MouseOrPenMove(pointRelativeToInkCanvas, mouseOrPenDown);
    }

    //void ClientInkCanvas_PreviewMouseMove(object sender, MouseEventArgs e)
    //{
    //  // determine if mouse button is down
    //  bool mouseOrPenDown = (e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed ||
    //    e.MiddleButton == MouseButtonState.Pressed);

    //  // get the mouse position relative to the ink canvas
    //  Point pointRelativeToInkCanvas = e.GetPosition(this);
    //  MouseOrPenMove(pointRelativeToInkCanvas, mouseOrPenDown);
    //}

    // Right after a mouseUp a mouseMove is fired before a stroke is done being collected
    // we use this variable to make it so that a mode switch does not happen before the stroke
    // is done being collected if the stroke started on a Shape or Widget that has an edit mode assigned to it
    private bool _JustTriedSwitchedEditingMode = false;

    /// <summary>
    /// Mouse/Pen invarient move handler - Mouse or stylus onmove handlers should call this method
    /// </summary>
    /// <param name="pointRelativeToInkCanvas">the location of the mouse or stylus relative to the InkCanvas</param>
    /// <param name="mouseOrPenDown">should be true if any mouse button is pressed, the stylus is down, 
    /// or any barrel button is pressed</param>
    protected void MouseOrPenMove(Point pointRelativeToInkCanvas, bool mouseOrPenDown)
    {
      // if the pen/mouse is not down we need to check to see if we want to change the editing mode
      // we don't change the editing mode when the mouse is down because an edit has already begun
      if (!mouseOrPenDown)
      {
        bool changedEditMode = false;

        // commented out for now

        // check each of our children and see if they have a diffrent editing mode specified
        //foreach (UIElement uie in this.Children)
        //{
        //  Point pointRelativeToChild = this.TranslatePoint(pointRelativeToInkCanvas, uie);
        //  // if we are over this child
        //  if (uie.InputHitTest(pointRelativeToChild) != null)
        //  {
        //    // if they have an editing mode set it
        //    if (this.EditingMode != InkCanvasEditingMode.GestureOnly)
        //    {
        //      this.EditingMode = InkCanvasEditingMode.GestureOnly;
        //    }
        //    changedEditMode = true;
        //  }
        //}

        // if we are no longer over something with a specific editing mode use the current editing mode

        // commented out for now

        //if (!changedEditMode)
        //{
        //  if (this.EditingMode != InkCanvasEditingMode.Ink)
        //  {
        //    // Right after a mouseUp a mouseMove is fired before a stroke is done being collected
        //    // we use _JustTriedSwitchedEditingMode to make it so that a mode switch does not happen before the stroke
        //    // is done being collected if the stroke started on a Shape or Widget that has an edit mode assigned to it
        //    if (_JustTriedSwitchedEditingMode)
        //    {
        //      //this.EditingMode = InkCanvasEditingMode.Ink;
        //      _JustTriedSwitchedEditingMode = false;
        //    }
        //    else
        //    {
        //      _JustTriedSwitchedEditingMode = true;
        //    }
        //  }
        //}
      }
    }

    void ClientInkCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
      //this.EditingMode = InkCanvasEditingMode.Ink;
    }

    public delegate void CommandOccuredDelegate(Ink.SketchCommands.SketchCommand wozClientCommand);

    public event CommandOccuredDelegate CommandOccured;

    void ClientInkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
    {
      Stroke stroke = e.Stroke;

      // Replace the stroke with a path
      //ReplaceStrokeWithPath(stroke);

      // Replace the stroke with a WozStroke
      SketchStroke wozStroke = ReplaceStrokeWithWozStroke(stroke);

      // create CreateCommand
      CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(wozStroke.GetWozObject());

      // push creation command
      NotifyCommandOccured(wozClientCreateCommand);
    }

    private void NotifyCommandOccured(Ink.SketchCommands.SketchCommand wozClientCreateCommand)
    {
      if (CommandOccured != null)
      {
        CommandOccured(wozClientCreateCommand);
      }
    }

    SketchStroke ReplaceStrokeWithWozStroke(Stroke stroke)
    {
      // remove the stroke because we are going to create a path out of it
      this.Strokes.Remove(stroke);

      // Create a path using the stroke's geometry
      SketchStroke wozStroke = new SketchStroke(stroke, this.SketchObjectRegistry);

      // add the new path to our children
      this.Strokes.Add(wozStroke);

      return wozStroke;
    }

    void ReplaceStrokeWithPath(Stroke stroke)
    {
      // remove the stroke because we are going to create a path out of it
      this.Strokes.Remove(stroke);

      // Create a path using the stroke's geometry
      Path path = new Path();
      path.Data = stroke.GetGeometry();
      path.Fill = Brushes.Black;
      path.StrokeThickness = stroke.DrawingAttributes.Width;

      // add the new path to our children
      this.Children.Add(path);
    }

    // for use with showing "fake" ink
    private bool _InkingOverlay = false;
    private Stroke _InkingOverlayStroke;
    private DrawingAttributes _InkingOverlayDrawingAttributes;
    ClientMouseAdorner _ClientMouseAdorner;
    public override void Overlay(Point Position, MouseButtonState LeftButton, MouseButtonState MiddleButton, MouseButtonState RightButton)
    {
      AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);

      if (_ClientMouseAdorner == null)
      {
        _ClientMouseAdorner = new ClientMouseAdorner(this, new Point(Position.X, Position.Y), UserCurrentInkTool);
        adornerLayer = AdornerLayer.GetAdornerLayer(this);
        if (adornerLayer != null)
        {
          adornerLayer.Add(_ClientMouseAdorner);
        }
      }
      else if (adornerLayer != null && adornerLayer.GetAdorners(this) == null)
      {
        adornerLayer.Add(_ClientMouseAdorner);
      }
      else if (adornerLayer != null)
      {
        bool inThere = false;
        foreach (Adorner adorner in adornerLayer.GetAdorners(this))
        {
          if (adorner == _ClientMouseAdorner)
          {
            inThere = true;
            break;
          }
        }

        if (!inThere)
        {
          adornerLayer.Add(_ClientMouseAdorner);
        }
      }

      // "true ||" is used to not hide mouse cursor during drawing
      if (true || LeftButton == MouseButtonState.Released)
      {
        _ClientMouseAdorner.PlacementCenterPoint = new Point(Position.X, Position.Y);
        _ClientMouseAdorner.InkTool = UserCurrentInkTool;
        _ClientMouseAdorner.Visibility = Visibility.Visible;

        // we check AdornerLayer.GetAdornerLayer(this) for null because
        // when this is not the top screen there is no adorner to use for
        // drawing the mouse position
        if (adornerLayer != null && adornerLayer.GetAdorners(this) != null)
        {
          adornerLayer.Update(this);
        }
      }
      else
      {
        _ClientMouseAdorner.Visibility = Visibility.Hidden;

        // we check AdornerLayer.GetAdornerLayer(this) for null because
        // when this is not the top screen there is no adorner to use for
        // drawing the mouse position
        if (adornerLayer != null && adornerLayer.GetAdorners(this) != null)
        {
          adornerLayer.Update(this);
        }
      }

      // make sure the inking over lay attributes are instantiated
      if (_InkingOverlayDrawingAttributes == null)
      {
        _InkingOverlayDrawingAttributes = new DrawingAttributes();
        //_InkingOverlayDrawingAttributes.IsHighlighter = true;
      }

      // figure out what the ink should look like
      // !!! for effeciency maybe change this only once per mouse up / mouse down
      switch (UserCurrentInkTool)
      {
        case SketchPad.InkTools.Pencil:
          _InkingOverlayDrawingAttributes.Color = Colors.Black;
          _InkingOverlayDrawingAttributes.Width = 2;
          _InkingOverlayDrawingAttributes.Height = 2;
          _InkingOverlayDrawingAttributes.IsHighlighter = false;
          break;
        case SketchPad.InkTools.Eraser:
          _InkingOverlayDrawingAttributes.Color = Colors.Transparent;
          _InkingOverlayDrawingAttributes.Width = 4;
          _InkingOverlayDrawingAttributes.Height = 4;
          _InkingOverlayDrawingAttributes.IsHighlighter = false;
          break;
        case SketchPad.InkTools.Selector:
          _InkingOverlayDrawingAttributes.Color = Colors.Gray;
          _InkingOverlayDrawingAttributes.Width = 4;
          _InkingOverlayDrawingAttributes.Height = 4;
          _InkingOverlayDrawingAttributes.IsHighlighter = true;
          break;
        case SketchPad.InkTools.None:
          _InkingOverlayDrawingAttributes.Color = Colors.Transparent;
          _InkingOverlayDrawingAttributes.Width = 4;
          _InkingOverlayDrawingAttributes.Height = 4;
          _InkingOverlayDrawingAttributes.IsHighlighter = false;
          break;
        case SketchPad.InkTools.Gesture:
          _InkingOverlayDrawingAttributes.Color = Colors.Orange;
          _InkingOverlayDrawingAttributes.Width = 4;
          _InkingOverlayDrawingAttributes.Height = 4;
          _InkingOverlayDrawingAttributes.IsHighlighter = true;
          break;
        default:
          _InkingOverlayDrawingAttributes.Color = Colors.Black;
          _InkingOverlayDrawingAttributes.Width = 2;
          _InkingOverlayDrawingAttributes.Height = 2;
          _InkingOverlayDrawingAttributes.IsHighlighter = false;
          break;
      }

      lock (_InkingOverlayDrawingAttributes)
      {
        if (LeftButton == MouseButtonState.Pressed && !_InkingOverlay)
        {
          _InkingOverlay = true;

          StylusPointCollection stylusPointCollection = new StylusPointCollection();
          stylusPointCollection.Add(new StylusPoint(Position.X, Position.Y));
          _InkingOverlayStroke = new Stroke(stylusPointCollection, _InkingOverlayDrawingAttributes);
          this.Strokes.Add(_InkingOverlayStroke);
        }
        else if (LeftButton == MouseButtonState.Pressed)
        {
          this.Suspended = true;
          _InkingOverlayStroke.StylusPoints.Add(new StylusPoint(Position.X, Position.Y));
          this.Suspended = false;
          this.InvalidateVisual();
        }
        else if (LeftButton == MouseButtonState.Released && _InkingOverlay)
        {
          _InkingOverlay = false;
          this.Strokes.Remove(_InkingOverlayStroke);
        }
      }
    }




  } //end class
}
